<?php


class ArrayDiff {
	const STATUS_KEY = 'diff_status';
	const STATUS_NONE = 0;
	const STATUS_APPEND = 1;
	const STATUS_UPDATE = 2;
	const STATUS_DELETE = 3;

	/**
	 * Two-dimensional array diff : the key is table name, value is table records
	 * The $keys is table primary keys
	 *
	 * Ex:  
     *     $new_data = array(
	 *          'programme'=> array(
     *                array('id'=>1, 'other_keys..'=>'other_values..'),
     *                array('id'=>2, 'other_keys..'=>'other_values..'),
     *                array('id'=>4, 'other_keys..'=>'other_values..')
     *           ),
     *          'other_tables..'=>array(  other_records  )
     *     );
     *     $old_data = array(
	 *          'programme'=> array(
     *                array('id'=>1, 'other_keys..'=>'other_values..'),
     *                array('id'=>2, 'other_keys..'=>'other_values..changed'),
     *                array('id'=>3, 'other_keys..'=>'other_values..')
     *           ),
     *          'other_tables..'=>array(  other_records  )
     *     );
     *     $diff = ArrayDiff($new_data,$old_data);
     *
     *  $diff is :
     *  array(
	 *     'programme'=> array(
     *           array('id'=>1, 'other_keys..'=>'other_values..','status'=>'none'), // no change
     *           array('id'=>2, 'other_keys..'=>'other_values..','status'=>'update'), // have updated
     *           array('id'=>4, 'other_keys..'=>'other_values..','status'=>'append'),
     *           array('id'=>3, 'other_keys..'=>'other_values..','status'=>'delete'),
     *      ),
     *     'other_tables..'=>array(  other_records  )
     *  )    
	 *
	 * @param array $keys Ex: array('programme'=>array('id')) default using 'id'
     */
	static public function multi_diff(&$a,&$b){//,$keys=array()){

        $new = array();

		if(empty($a) || empty($b)) return $new;
		
        foreach($a as $_k => & $_v1)  // programme => array(array())
        {
			$keys = CommonUtils::get_table_keys($_k);
            #if(!isset($keys[$_k])){ $keys[$_k] = array('id'); }

            foreach($_v1 as $index => $data)  // 0 => array(id,name)
            {
				$old_data = array();
				$uniqid   = null;
                $status   = self::_multi_item_status($keys, $data, $b[$_k], 'in', $old_data,$uniqid);
                #$status   = self::_multi_item_status($keys[$_k], $data, $b[$_k], 'in', $old_data,$uniqid);
                if(self::STATUS_APPEND == $status){
                    $data[self::STATUS_KEY] = self::STATUS_APPEND;
                    #$new[$_k][$index] = $data;
                    $new[$_k][$uniqid] = $data;
                }else{
                    #$old_data[self::STATUS_KEY] = $status;
                    #$new[$_k][$index] = $old_data;
                    #$new[$_k][$uniqid] = $old_data;
                    $data[self::STATUS_KEY] = $status;
                    $new[$_k][$uniqid] = $data;
                }
            }
        }

        foreach($b as $_k => & $_v)  // programme => array(array())
        {
			$keys = CommonUtils::get_table_keys($_k);
			if($_v)
            foreach($_v as $index => $data)  // 0 => array(id,name)
            {
                #if(!isset($keys[$_k])){ $keys[$_k] = array('id'); }

				$old_data = array();
				$uniqid   = null;
                $status = self::_multi_item_status($keys, $data, $a[$_k], 'out',$old_data,$uniqid);
                #$status = self::_multi_item_status($keys[$_k], $data, $a[$_k], 'out',$old_data,$uniqid);
                $data[self::STATUS_KEY] = $status;
                if(self::STATUS_DELETE == $status){
                    #$new[$_k][] = $data;
                    $new[$_k][$uniqid] = $data;
                }
            }
        }
        return $new;
	}

	/**
	 * private function for multi_diff()
	 */
	static private function _multi_item_status($keys,&$a,&$b,$type='in',&$old_data=array(),&$uniqid){

		$old_data = self::_find_item($keys,$a,$b,$uniqid);

		if($old_data){
			$s = ($a==$old_data) ? self::STATUS_NONE : self::STATUS_UPDATE;
		}else{
			if('in'==$type){
				$s = self::STATUS_APPEND;
			}elseif('out'==$type){
				$s = self::STATUS_DELETE;
			}
		}

		return $s;
	}

	/**
	 * private function
	 */
	static private function _find_item(&$keys,&$a,&$b,&$val){
		$temp_c = array();
		if(!is_array($keys)) $keys = array($keys);

		$val    = '';

		foreach($keys as $k){
			if(isset($a[$k])) $val .= $a[$k];
		}
		if(empty($val)){
			$real_keys = array(key($a));
			$val  = current($a);
			reset($a);
		}else{
			$real_keys = &$keys;
		}
		
		if($b)
		foreach($b as $index => $data){
			$new = '';
			foreach($real_keys as $k){
				$new .= $data[$k];
			}

			if($new == $val){
				$temp_c = $data;
				break;
			}
		}
		return $temp_c;
	}

	static public function array_diff_by_data(&$data,&$a,&$b,$keys=array()){
		return slef::array_diff($a,$b,$keys,$data);
	}

	/**
	 *
	 * return array(
	 *     
	 *         array(
	 *             'name' => false // true is Highlight
	 *         ),
	 * )
	 */
	#static public function array_diff($a,$b,$keys=array(),&$data=null){
	static public function array_diff(&$a,&$b,&$old_data=null){
		if(empty($old_data)){
			$diff = self::multi_diff($a,$b);
			#$diff = self::multi_diff($a,$b,$keys);
		}else{
			$diff = & $old_data;
		}

		$result = array();
		$arr_keys = array();

		foreach($diff as $k=>& $v){
			if( !isset($arr_keys[$k]) && current($v) ){
				$temp = current($v);
				unset($temp[self::STATUS_KEY]);
				$arr_keys[$k] = array_keys($temp);
				unset($temp);
				reset($v);
			}

			$keys = CommonUtils::get_table_keys($k);
			#if(!isset($keys[$k])){ 
			#	$keys[$k] = array('id'); 
			#}

			foreach($v as $index => $data)
			{

				$status = $data[self::STATUS_KEY];

				if($status == self::STATUS_APPEND || $status == self::STATUS_NONE) // no Highlight
				{
					$result[$k][$index] = array_fill_keys($arr_keys[$k],false);
				}
				else if($status == self::STATUS_UPDATE)
				{
					$uniqid = null;
					$old = $data;
					$new = self::_find_item($keys, $data, $b[$k], $uniqid);
					#$new = self::_find_item($keys[$k], $data, $a[$k], $uniqid);

					unset($new[self::STATUS_KEY],$old[self::STATUS_KEY]);	

					$change_item = array_diff_assoc($new,$old);
					$_array = array();
					//$result[$k][] = array_fill_keys(array_keys($change_item),true);
					foreach($arr_keys[$k] as $__k){
						if(!isset($change_item[$__k])){
							$_array[$__k] = false;
						}else{
							$_array[$__k] = true;
						}
					}
					$result[$k][$uniqid] = $_array;
				}
			}
		}
		#echo "<!-- ",print_r($b,1), " -->";
		#echo "<!-- ",print_r($a,1), print_r($b,1), print_r($diff,1), " -->";
		return $result;
	}
}


/*
$a = array(
    'programme' => array(
        array( 'id'=> 8, 'name'=>'vanni'),
        array( 'id'=> 1, 'name'=>'vanni'),
        array( 'id'=> 2, 'name'=>'ann')
    ),
    'cast' => array(
        array( 'first_name'=>'jackie', 'last_name'=>'lee'),
        array( 'first_name'=>'jackie', 'last_name'=>'lee3')
    ),
    'programme_cpl'=> array(
        array('p_id'=>1, 'c_id'=>2),
        array('p_id'=>1, 'c_id'=>3)
    )
);

$b = array(
    'programme' => array(
        array( 'id'=> 9, 'name'=>'vanni fan'),
        array( 'id'=> 1, 'name'=>'vanni fan'),
        array( 'id'=> 3, 'name'=>'simon')
    ),
    'cast' => array(
        array( 'first_name'=>'jackie', 'last_name'=>'lee2'),
        array( 'first_name'=>'jackie', 'last_name'=>'lee')
    ),
    'programme_cpl'=> array(
        array('p_id'=>1, 'c_id'=>2),
        array('p_id'=>1, 'c_id'=>4)
    )
);

//$c = ArrayDiff::multi_diff($a,$b);//,array('cast'=>'last_name','programme_cpl'=>array('p_id','c_id')));
$c = ArrayDiff::multi_diff($a,$b,array('cast'=>'last_name','programme_cpl'=>array('p_id','c_id')));
$d = ArrayDiff::array_diff($a,$b,array('cast'=>'last_name','programme_cpl'=>array('p_id','c_id')));
print_r($c);
print_r($d);
 */
